package com.hw.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hw.shortlink.admin.common.biz.user.UserContext;
import com.hw.shortlink.admin.common.constant.RedisCacheConstants;
import com.hw.shortlink.admin.common.convention.exception.ClientException;
import com.hw.shortlink.admin.common.enums.UserErrorCodeEnum;
import com.hw.shortlink.admin.dao.entity.User;
import com.hw.shortlink.admin.dao.mapper.UserMapper;
import com.hw.shortlink.admin.dto.req.GroupSaveReqDto;
import com.hw.shortlink.admin.dto.req.UserLoginReqDto;
import com.hw.shortlink.admin.dto.req.UserRegisterReqDto;
import com.hw.shortlink.admin.dto.req.UserUpdateReqDto;
import com.hw.shortlink.admin.dto.resp.UserLoginRespDto;
import com.hw.shortlink.admin.dto.resp.UserRespDto;
import com.hw.shortlink.admin.service.GroupService;
import com.hw.shortlink.admin.service.UserService;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.hw.shortlink.admin.common.constant.RedisCacheConstants.USER_LOGIN_KEY;
import static com.hw.shortlink.admin.common.enums.UserErrorCodeEnum.*;

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {

    private final RBloomFilter<String> userRegisterCachePenetrationBloomFilter;

    private final RedissonClient redissonClient;

    private final StringRedisTemplate stringRedisTemplate;

    private final GroupService groupService;


    @Override
    public UserRespDto getUserByUsername(String username) {
        User user = this.lambdaQuery().eq(User::getUsername,username).one();

        if(user==null){
            throw new ClientException(UserErrorCodeEnum.USER_NULL);
        }
        return BeanUtil.copyProperties(user,UserRespDto.class);
    }


    @Override
    public Boolean availableUsername(String username) {
        return !userRegisterCachePenetrationBloomFilter.contains(username);
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(UserRegisterReqDto userRegisterReqDto) {

        String username = userRegisterReqDto.getUsername();
        if(!availableUsername(username)){
            throw new ClientException(UserErrorCodeEnum.USER_NAME_EXIST);
        }

        //符合条件，上锁
        RLock lock = redissonClient.getLock(RedisCacheConstants.LOCK_USER_REGISTER_KEY + username);
        if(!lock.tryLock()){
            throw new ClientException(UserErrorCodeEnum.USER_EXIST);
        }

        try {
            User user=BeanUtil.copyProperties(userRegisterReqDto,User.class);
            boolean flag = save(user);
            if(!flag){
                throw new ClientException(UserErrorCodeEnum.USER_SAVE_ERROR);
            }


            //创建默认分组
            GroupSaveReqDto groupSaveReqDto = new GroupSaveReqDto();
            groupSaveReqDto.setName("default");
            groupService.createGroup(user.getUsername(),groupSaveReqDto);


            userRegisterCachePenetrationBloomFilter.add(username);
        } catch (ClientException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }

    }

    @Override
    public void update(UserUpdateReqDto userUpdateReqDto) {

        if(!Objects.equals(userUpdateReqDto.getUsername(), UserContext.getUsername())){
            throw new ClientException(USER_NOT_LOGIN);
        }

        User user = BeanUtil.copyProperties(userUpdateReqDto, User.class);
        update(user,new LambdaUpdateWrapper<User>().eq(User::getUsername,user.getUsername()));
    }

    @Override
    public UserLoginRespDto login(UserLoginReqDto userLoginReqDto) {

        String username=userLoginReqDto.getUsername();
        String password=userLoginReqDto.getPassword();

        User user = lambdaQuery().eq(User::getUsername, username)
                .eq(User::getDelFlag,0).one();
        //用户名错误
        if(user==null){
            throw new ClientException(USER_NOT_REGISTER);
        }

       //密码错误
        if(!Objects.equals(user.getPassword(), password)){
            throw new ClientException(USER_PASSWORD_ERROR);
        }

        Boolean hasLogin = stringRedisTemplate.hasKey(USER_LOGIN_KEY + username);
        if(Boolean.TRUE.equals(hasLogin)){
            throw new ClientException(USER_ALREADY_LOGIN);
        }


        //生成token
        UUID uuid = UUID.randomUUID();
        String token = uuid.toString(true);



        stringRedisTemplate.opsForHash().put(USER_LOGIN_KEY+username,token, JSON.toJSONString(user));
        stringRedisTemplate.expire(USER_LOGIN_KEY+username,30, TimeUnit.DAYS);
        return new UserLoginRespDto(token);
    }


    @Override
    public Boolean checkLogin(String username,String token) {

        return stringRedisTemplate.opsForHash().get(USER_LOGIN_KEY + username, token) != null;
    }

    @Override
    public void logout(String username, String token) {
        if (!checkLogin(username, token)) {
            throw new ClientException(USER_NOT_LOGIN);
        }


        stringRedisTemplate.delete(USER_LOGIN_KEY + username);
    }
}
